/*
GANESHA - Reasoning on critical infrastructures affected by emergency situations
Copyright (c) 2010-2011, Information Eng. Research Unit - Univ. of Alcala
http://www.cc.uah.es/ie
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option)
any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along
with this library; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package es.uah.cc.ie.ganesha;

import com.hp.hpl.jena.ontology.Individual;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import es.uah.cc.ie.ganesha.messaging.AlertMessage;
import es.uah.cc.ie.ganesha.messaging.position.MessagePosition;
import es.uah.cc.ie.ganesha.messaging.position.PointPosition;
import java.util.ArrayList;
import java.util.List;

/**
 * Given an incident, looks for nodes that are near to the place of the incident and could
 * be affected by it.
 * @author ieru
 */
public abstract class AffectedNodeSearch {

	private static final String HAS_LOCATION_PROP_URI = "http://www.ieru.org/ont/emergency/BFiaO#hasLocation";
	private static final String HAS_COORDINATE_PROP_URI = "http://www.ieru.org/ont/emergency/BFiaO#hasCoordinate";
	private static final String LAT_PROP_URI = "http://www.ieru.org/ont/emergency/BFiaO#lat";
	private static final String LON_PROP_URI = "http://www.ieru.org/ont/emergency/BFiaO#lon";
	private static final double NEARBY_DISTANCE = 10; //in kilometers

	/**
	 * Looks for all nodes that are near to a given incident.
	 * @param incidente Incident.
	 * @param allNodes All nodes to be checked.
	 * @return Returns all nodes that are near to a given incident.
	 */
	public static List<Individual> getNearbyNodes(AlertMessage incidente, List<Individual> allNodes) {
		List<Individual> result = new ArrayList<Individual>();

		if (allNodes.isEmpty()) {
			return result;
		}

		assert (incidente.getPositions().size() > 0);

		//Ontology stuff
		OntModel ontology = allNodes.get(0).getOntModel();
		Property hasLocationProp = ontology.getProperty(HAS_LOCATION_PROP_URI);
		Property hasCoordinateProp = ontology.getProperty(HAS_COORDINATE_PROP_URI);
		Property latProp = ontology.getProperty(LAT_PROP_URI);
		Property lonProp = ontology.getProperty(LON_PROP_URI);

		//Distance measure stuff
		final double earthRadius = 6371f;
		double dLat = 0f, dLon = 0f;
		double a = 0f, c = 0f, d = 0f;
		double lon1 = 0.0, lat1 = 0.0;
		double lon2 = 0f, lat2 = 0f;

		//Check every node
		for (Individual node : allNodes) {
			RDFNode hasLocationValue = node.getPropertyValue(hasLocationProp);
			Individual nodeLocation = (Individual) ontology.getIndividual(hasLocationValue.toString());

			RDFNode hasCoordinateValue = nodeLocation.getPropertyValue(hasCoordinateProp);
			Individual nodeCoordinate = (Individual) ontology.getIndividual(hasCoordinateValue.toString());

			lat2 = nodeCoordinate.getProperty(latProp).getDouble();
			lon2 = nodeCoordinate.getProperty(lonProp).getDouble();

			//Check every position
			for (MessagePosition pos : incidente.getPositions()) {
				//If position is a point, check if it's in the NEARBY_DISTANCE
				if (pos instanceof PointPosition) {
					lon1 = pos.getCenterLongitude();
					lat1 = pos.getCenterLatitude();

					dLat = Math.toRadians(lat2 - lat1);
					dLon = Math.toRadians(lon2 - lon1);

					a = Math.sin(dLat / 2.0) * Math.sin(dLat / 2.0) +
							Math.cos(lat1) * Math.cos(lat2) *
							Math.sin(dLon / 2.0) * Math.sin(dLon / 2.0);

					c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
					d = earthRadius * c;

					if (d < NEARBY_DISTANCE) {
						result.add(node);
						break; //stop checking this node
					}
				} else {
					if (pos.isInArea(lat2, lon2)) {
						result.add(node);
						break; //stop checking this node
					}
				}
			}
		}

		return result;
	}

	/**
	 * Filters nodes by the incident type that can affect them, according to the type of a given incident.
	 * @param incident Incident according to which the nodes will be filtered.
	 * @param nodes Nodes to be filtered
	 * @return Returns all the nodes that can be affected by the given incident according to its event type.
	 */
	public static List<Individual> filterNodesByType(AlertMessage incident, List<Individual> nodes) {
		String eventType = incident.getEventType();
		List<String> notAllowedNodeType = new ArrayList<String>();
		List<Individual> result = new ArrayList<Individual>();

		if (eventType.equals("INCENDIO")) {
			//TODO:anyadir tipos de nodos NO permitidos
			notAllowedNodeType.add("");
		}

		for (Individual node : nodes) {
			//TODO: comprobar si el tipo del nodo esta entre los tipos NO permitidos y si lo esta, quitarle
			/*
			if(!notAllowedNodeType.contains(node.tipo))
			{
			result.add(node);
			}
			 */
		}

		return result;
	}
}
